"""
核心工具模块
包含所有公共工具函数、常量定义和数据结构

作者：wjttdbx
版本：3.0 - 重构版本，统一工具函数
"""
import math
from re import T
import numpy as np
from typing import List, Tuple, Optional
from dataclasses import dataclass
from .config import GameConfig


# ===== 地图坐标常量定义 =====
# 根据题目要求：A1A2=A6A5=200km, A3A4=A7A8=500km, A3A8=A4A7=300km, A1A8=A2A3=A6A7=A5A4=100km
# 建立坐标系：左下角是原点，x轴向右，y轴向上

# 计算各点坐标
A1 = (50000, 500000 + 100000 * math.sqrt(3))
A2 = (250000, 500000 + 100000 * math.sqrt(3))  # A1右侧200km
A3 = (300000, 500000 + 50000 * math.sqrt(3))   # A2向下50√3km，向右50km
A4 = (300000, 50000 * math.sqrt(3))            # A3向下500km
A5 = (250000, 0)                               # A4向左50km,向下50√3km
A6 = (50000, 0)                                # A5向左200km
A7 = (0, 50000 * math.sqrt(3))                 # A6向左50km，向上50√3km
A8 = (0, 500000 + 50000 * math.sqrt(3))        # 在A7向上500km

# 实际的凸八边形顶点（按逆时针顺序）
POLYGON_POINTS = [A1, A2, A3, A4, A5, A6, A7, A8]

# A7A8线段（白方部署线）
DEPLOYMENT_LINE_START = A7  # (0, 50000*sqrt(3))
DEPLOYMENT_LINE_END = A8    # (0, 500000+50000*sqrt(3))

# A2A3A4A5梯形区域（黑方初始化区域）
TRAPEZOID_POINTS = [A2, A3, A4, A5]

# A1A6红线（突破线）- 从A1到A6的线段
BREAKTHROUGH_LINE_START = A1  # (50000, 500000+100000*sqrt(3))
BREAKTHROUGH_LINE_END = A6    # (50000, 0)


# ===== 数据结构定义 =====
@dataclass
class DetectionResult:
    """
    探测结果数据结构
    
    用于存储探测到的目标信息，替代原来的targets类
    
    Attributes:
        target_ids (List[int]): 探测到的目标ID列表
        positions (List[Tuple[float, float]]): 目标位置列表
        distances (List[float]): 目标距离列表
    """
    target_ids: List[int]
    positions: List[Tuple[float, float]]
    distances: List[float]
    
    def __init__(self):
        self.target_ids = []
        self.positions = []
        self.distances = []
    
    def add_target(self, target_id: int, position: Tuple[float, float], distance: float):
        """添加探测目标"""
        self.target_ids.append(target_id)
        self.positions.append(position)
        self.distances.append(distance)
    
    def clear(self):
        """清空探测结果"""
        self.target_ids.clear()
        self.positions.clear()
        self.distances.clear()
    
    def get_count(self) -> int:
        """获取探测到的目标数量"""
        return len(self.target_ids)


# ===== 几何工具函数 =====
def point_in_polygon(x: float, y: float, polygon: List[Tuple[float, float]]) -> bool:
    """
    判断点(x, y)是否在多边形polygon内
    
    使用射线法（Ray Casting Algorithm）进行点在多边形内的判断
    
    Args:
        x (float): 点的X坐标
        y (float): 点的Y坐标
        polygon (List[Tuple[float, float]]): 多边形顶点列表
        
    Returns:
        bool: True表示点在多边形内，False表示在外
    """
    num = len(polygon)
    j = num - 1
    inside = False
    for i in range(num):
        xi, yi = polygon[i]
        xj, yj = polygon[j]
        if ((yi > y) != (yj > y)) and \
           (x < (xj - xi) * (y - yi) / (yj - yi + 1e-8) + xi):
            inside = not inside
        j = i
    return inside


def point_to_line_segment_distance(px: float, py: float,
                                 x1: float, y1: float, x2: float, y2: float) -> float:
    """
    计算点到线段的最短距离
    
    Args:
        px, py: 点坐标
        x1, y1, x2, y2: 线段两端点坐标
    
    Returns:
        float: 点到线段的最短距离
    """
    nearest_x, nearest_y = point_to_line_segment_nearest_point(px, py, x1, y1, x2, y2)
    return math.sqrt((px - nearest_x)**2 + (py - nearest_y)**2)


def point_to_line_segment_nearest_point(px: float, py: float,
                                       x1: float, y1: float, x2: float, y2: float) -> Tuple[float, float]:
    """
    计算点到线段的最近点
    
    Args:
        px, py: 点坐标
        x1, y1, x2, y2: 线段两端点坐标
    
    Returns:
        Tuple[float, float]: 线段上最近点的坐标
    """
    # 线段向量
    dx = x2 - x1
    dy = y2 - y1
    
    if dx == 0 and dy == 0:
        # 线段退化为点
        return x1, y1
    
    # 计算投影参数t
    t = ((px - x1) * dx + (py - y1) * dy) / (dx * dx + dy * dy)
    
    # 限制t在[0,1]范围内
    t = max(0, min(1, t))
    
    # 计算最近点
    nearest_x = x1 + t * dx
    nearest_y = y1 + t * dy
    
    return nearest_x, nearest_y


def distance_between_points(x1: float, y1: float, x2: float, y2: float) -> float:
    """
    计算两点间的欧几里得距离
    
    Args:
        x1, y1: 第一个点坐标
        x2, y2: 第二个点坐标
        
    Returns:
        float: 两点间距离
    """
    return math.sqrt((x1 - x2)**2 + (y1 - y2)**2)


def normalize_angle(angle: float) -> float:
    """
    将角度标准化到[0, 2π]范围内
    
    Args:
        angle (float): 输入角度（弧度）
        
    Returns:
        float: 标准化后的角度
    """
    return angle % (2 * math.pi)


def angle_difference(angle1: float, angle2: float) -> float:
    """
    计算两个角度之间的差值，结果在[-π, π]范围内
    
    Args:
        angle1 (float): 第一个角度（弧度）
        angle2 (float): 第二个角度（弧度）
        
    Returns:
        float: 角度差值
    """
    diff = angle1 - angle2
    while diff > math.pi:
        diff -= 2 * math.pi
    while diff < -math.pi:
        diff += 2 * math.pi
    return diff





def is_in_sector(center_x: float, center_y: float, center_heading: float,
                target_x: float, target_y: float, sector_angle: float, max_range: float) -> bool:
    """
    判断目标是否在扇形探测区域内
    
    Args:
        center_x, center_y: 扇形中心点坐标
        center_heading: 扇形中心方向（弧度）
        target_x, target_y: 目标坐标
        sector_angle: 扇形角度（弧度）
        max_range: 最大探测距离
        
    Returns:
        bool: 目标是否在扇形内
    """
    # 计算距离
    distance = distance_between_points(center_x, center_y, target_x, target_y)
    if distance > max_range:
        return False
    
    # 计算目标相对于中心点的角度
    target_angle = math.atan2(target_y - center_y, target_x - center_x)
    
    # 计算角度差
    angle_diff = abs(angle_difference(target_angle, center_heading))
    
    # 检查是否在扇形内
    return angle_diff <= sector_angle / 2


# ===== 地图相关工具函数 =====
def is_in_battle_area(x: float, y: float) -> bool:
    """
    判断坐标是否在战斗区域内（凸八边形）
    
    Args:
        x, y: 坐标
        
    Returns:
        bool: 是否在战斗区域内
    """
    return point_in_polygon(x, y, POLYGON_POINTS)


def is_in_white_deployment_area(x: float, y: float, tolerance: float = 1000) -> bool:
    """
    判断坐标是否在白方部署区域附近
    
    Args:
        x, y: 坐标
        tolerance: 容差范围（米）
        
    Returns:
        bool: 是否在部署区域附近
    """
    distance_to_line = point_to_line_segment_distance(
        x, y, 
        DEPLOYMENT_LINE_START[0], DEPLOYMENT_LINE_START[1],
        DEPLOYMENT_LINE_END[0], DEPLOYMENT_LINE_END[1]
    )
    return distance_to_line <= tolerance


def is_in_black_init_area(x: float, y: float) -> bool:
    """
    判断坐标是否在黑方初始化区域内
    
    Args:
        x, y: 坐标
        
    Returns:
        bool: 是否在黑方初始化区域内
    """
    return point_in_polygon(x, y, TRAPEZOID_POINTS)


def check_breakthrough(x: float) -> bool:
    """
    检查是否突破封锁线（A1A6红线）
    
    Args:
        x: X坐标
        
    Returns:
        bool: 是否突破封锁线
    """
    return x <= 50000  # A1A6红线是x=50000的垂直线


# ===== 游戏状态工具函数 =====
def calculate_threat_level(distance_to_breakthrough: float, speed: float) -> float:
    """
    计算威胁等级
    
    基于到突破线的距离和速度计算威胁等级
    
    Args:
        distance_to_breakthrough: 到突破线的距离
        speed: 移动速度
        
    Returns:
        float: 威胁等级 (0-1)
    """
    if distance_to_breakthrough <= 0:
        return 1.0  # 已突破，最高威胁
    
    # 基于距离的威胁（距离越近威胁越高）
    distance_threat = max(0, 1 - distance_to_breakthrough / 100000)  # 100km作为参考距离
    
    # 基于速度的威胁（速度越快威胁越高）
    speed_threat = min(1, speed / GameConfig.BLACK_BOAT_MAX_SPEED)
    
    # 综合威胁等级
    return (distance_threat * 0.7 + speed_threat * 0.3)


def get_formation_position(boat_index: int, total_boats: int, 
                          center_x: float, center_y: float, spacing: float) -> Tuple[float, float]:
    """
    计算编队位置
    
    Args:
        boat_index: 船只索引
        total_boats: 总船只数量
        center_x, center_y: 编队中心
        spacing: 间距
        
    Returns:
        Tuple[float, float]: 编队位置坐标
    """
    # 计算偏移量
    offset = (boat_index - (total_boats - 1) / 2) * spacing
    
    # 线性编队（沿Y轴分布）
    return center_x, center_y + offset 